#include "platform/opengl/opengl_cubemap.hpp"

#include <stb/stb_image.h>

namespace Xen {

OpenGLCubeMap::OpenGLCubeMap() {
    GLCall(glGenTextures(1, &cubemap_));
    GLCall(glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_));
    GLCall(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GLCall(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GLCall(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLCall(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
    GLCall(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
}

OpenGLCubeMap::~OpenGLCubeMap() {
    GLCall(glDeleteTextures(1, &cubemap_));
}

GLenum TextureIndex2GLenum(CubeMap::TextureIndex idx) {
    return idx + GL_TEXTURE_CUBE_MAP_POSITIVE_X;
}

void OpenGLCubeMap::SetData(CubeMap::TextureIndex idx, const std::string& filepath) {
    int w, h, channels;
    stbi_set_flip_vertically_on_load(false);
    void* pixels = stbi_load(filepath.c_str(), &w, &h, &channels, 0);
    XEN_CORE_ASSERT(pixels, "file can't load: " + filepath);

    GLenum internal_format, data_format;
    if (channels == 1) {
        internal_format = GL_RED;
        data_format = GL_RED;
    } else if (channels == 3) {
        internal_format = GL_RGB8;
        data_format = GL_RGB;
    } else if (channels == 4) {
        internal_format = GL_RGBA8;
        data_format = GL_RGBA;
    }
    XEN_CORE_ASSERT(internal_format && data_format, "Invalid internal_format or data_format");

    GLCall(glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_));
    GLCall(glTexImage2D(TextureIndex2GLenum(idx), 0, internal_format, w, h, 0, data_format, GL_UNSIGNED_BYTE, (const void*)pixels));

    stbi_image_free(pixels);
}

void OpenGLCubeMap::SetData(CubeMap::TextureIndex idx, const void* data, uint32_t w, uint32_t h, uint32_t internal_format, uint32_t data_format) {
    GLCall(glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_));
    GLCall(glTexImage2D(TextureIndex2GLenum(idx), 0, internal_format, w, h, 0, data_format, GL_UNSIGNED_BYTE, (const void*)data));
}

void OpenGLCubeMap::Bind() {
    GLCall(glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_));
}

void OpenGLCubeMap::Unbind() {
    GLCall(glBindTexture(GL_TEXTURE_CUBE_MAP, 0));
}

}
